package com.ksyun.campus.metaserver.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ksyun.campus.metaserver.domain.DataServerInfo;
import com.ksyun.campus.metaserver.domain.FileInfo;
import com.ksyun.campus.metaserver.domain.FileType;
import com.ksyun.campus.metaserver.domain.Result;
import com.ksyun.campus.metaserver.mapper.FileInfoMapper;
import com.ksyun.campus.metaserver.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zsy
 * @since 2024-08-17
 */
@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements FileService {

    @Value("${server.rootPath}")
    private String rootPath;

    @Resource
    private FileInfoMapper fileInfoMapper;

    @Override
    @Transactional // 保证整个方法的事务性
    public boolean createFile(String fileSystemName, String path, List<DataServerInfo> dsList) {
        if (fileSystemName == null || fileSystemName.isEmpty()) {
            fileSystemName = "default";
        }
        // 确保 path 是以 “/” 开头，并将 fileSystemName 作为根目录
        if (!path.startsWith("/")) {
            path = "/" + path; // 自动添加 “/” 前缀
        }

        boolean isSuccess = true;
        String[] pathSegments = path.split("/");

        // 初始化父级目录路径
        String parentPath = rootPath + "/" + fileSystemName;

        // 遍历路径的每一级
        for (int j = 1; j < pathSegments.length; j++) { // 从1开始，忽略空字符串
            String currentPath = parentPath + "/" + pathSegments[j];
            boolean isLastSegment = (j == pathSegments.length - 1);
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileId(UUID.randomUUID().toString());
            fileInfo.setFileName(pathSegments[j]);
            fileInfo.setFolderType(isLastSegment ? FileType.File.getCode() : FileType.Directory.getCode());
            fileInfo.setFilePath(currentPath);
            fileInfo.setFilePid(parentPath); // 将父级目录路径设置为 filePid
            // 获取当前时间的时间戳（毫秒级）
            long currentTimestamp = System.currentTimeMillis();
            // 设置创建时间和更新时间
            fileInfo.setCreateTime(currentTimestamp);
            fileInfo.setUpdateTime(currentTimestamp);

            fileInfo.setFileSystemName(fileSystemName);

            // 遍历每一个数据服务器
            for (int i = 0; i < dsList.size(); i++) {
                DataServerInfo ds = dsList.get(i);
                String dsNode = "dataServer" + ds.getIpAddress() + "-" + ds.getPort();
                String dsCurrentPath = rootPath + "/" + dsNode + "/" + fileSystemName + path;

                System.out.println("创建文件: " + dsCurrentPath);
                File file = new File(dsCurrentPath);

                // 先创建必要的文件夹
                if (!file.getParentFile().exists()) {
                    if (!file.getParentFile().mkdirs()) {
                        log.error("文件创建失败: " + file.getParentFile().getAbsolutePath());
                        return false;
                    }
                    log.info("文件创建成功: " + file.getParentFile().getAbsolutePath());
                }

                if (!file.exists()) {
                    try {
                        if (isLastSegment) {
                            // 创建文件
                            file.createNewFile();
                            log.info("文件创建成功: " + dsCurrentPath);
                        }
                    } catch (IOException e) {
                        log.error("文件创建失败: " + dsCurrentPath);
                        return false;
                    }
                } else {
                    log.info("文件或文件夹已存在: " + dsCurrentPath);
                }

                // 记录副本位置信息
                if (i == 0) {
                    fileInfo.setCopyLocationOne("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                } else if (i == 1) {
                    fileInfo.setCopyLocationTwo("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                } else if (i == 2) {
                    fileInfo.setCopyLocationThree("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                }
            }

            // 将每一级路径的 FileInfo 保存到数据库
            if (!isFileExist(fileInfo)) {
                fileInfoMapper.insert(fileInfo);
            }

            // 更新父级目录路径
            parentPath = currentPath;
        }

        return isSuccess;
    }


    @Override
    @Transactional // 保证整个方法的事务性
    public boolean createFolder(String fileSystemName, String path, List<DataServerInfo> dsList) {
        if (fileSystemName == null || fileSystemName.isEmpty()) {
            fileSystemName = "default";
        }
        // 确保 path 是以 “/” 开头，并将 fileSystemName 作为根目录
        if (!path.startsWith("/")) {
            path = "/" + path; // 自动添加 “/” 前缀
        }

        boolean isSuccess = true;
        String[] pathSegments = path.split("/");

        // 初始化父级路径为 fileSystemName 根目录
        String parentPath = rootPath + "/" + fileSystemName;

        // 遍历路径的每一级
        for (int j = 1; j < pathSegments.length; j++) { // 从1开始，忽略空字符串
            String currentPath = parentPath + "/" + pathSegments[j];

            // 创建当前文件夹的 FileInfo
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFileId(UUID.randomUUID().toString());
            fileInfo.setFileName(pathSegments[j]);
            fileInfo.setFolderType(FileType.Directory.getCode());
            fileInfo.setFilePath(currentPath);

            // 获取当前时间的时间戳（毫秒级）
            long currentTimestamp = System.currentTimeMillis();
            // 设置创建时间和更新时间
            fileInfo.setCreateTime(currentTimestamp);
            fileInfo.setUpdateTime(currentTimestamp);

            fileInfo.setFileSystemName(fileSystemName);

            // 设置父级目录的 filePath 为当前目录的 filePid
            fileInfo.setFilePid(parentPath);

            // 遍历每一个数据服务器
            for (int i = 0; i < dsList.size(); i++) {
                DataServerInfo ds = dsList.get(i);
                String dsNode = "dataServer" + ds.getIpAddress() + "-" + ds.getPort();
                String dsCurrentPath = rootPath + "/" + dsNode + "/" + fileSystemName + currentPath.substring((rootPath + "/" + fileSystemName).length());

                File folder = new File(dsCurrentPath);

                if (!folder.exists()) {
                    if (!folder.mkdirs()) {
                        log.error("文件夹创建失败: " + dsCurrentPath);
                        return false;
                    }
                    log.info("文件夹创建成功: " + dsCurrentPath);
                }

                // 记录副本位置信息
                if (i == 0) {
                    fileInfo.setCopyLocationOne("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                } else if (i == 1) {
                    fileInfo.setCopyLocationTwo("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                } else if (i == 2) {
                    fileInfo.setCopyLocationThree("dataServer" + ds.getIpAddress() + "-" + ds.getPort());
                }
            }

            // 将每一级路径的 FileInfo 保存到数据库
            if (!isFileExist(fileInfo)) {
                fileInfoMapper.insert(fileInfo);
            }

            // 更新 parentPath 为当前路径，用于下一次循环
            parentPath = currentPath;
        }

        return isSuccess;
    }

    @Override
    public Result getListdir(String fileSystemName, String path) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        if (path.equals("/")) {
            path = "";
        }
        // 构建查询路径
        String fullPathInDatabase = rootPath + "/" + fileSystemName + path;

        // 查询所以pid为fullPathInDatabase的文件
        List<FileInfo> fileInfoList = fileInfoMapper.selectList(
                new LambdaQueryWrapper<FileInfo>()
                        .eq(FileInfo::getFilePid, fullPathInDatabase)
                        .eq(FileInfo::getFileSystemName, fileSystemName)
        );
        return Result.success(fileInfoList);
    }

    @Override
    public Result delete(String fileSystemName, String path) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        // 确保 path 是以 “/” 开头
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        // 构建完整的数据库查询路径
        String fullPath = rootPath + "/" + fileSystemName + path;

        // 当路径为 "/" 时，删除整个文件系统下的所有内容
        if ("/".equals(path)) {
            // 获取文件系统下的所有文件和文件夹
            List<FileInfo> allFiles = fileInfoMapper.selectList(new LambdaQueryWrapper<FileInfo>()
                    .eq(FileInfo::getFileSystemName, fileSystemName)
            );

            // 递归删除所有文件和文件夹
            for (FileInfo fileInfo : allFiles) {
                String relativePath = fileInfo.getFilePath().replaceFirst("^" + rootPath + "/" + fileSystemName, "");
                if (fileInfo.getFolderType() == 0) {
                    if (!deleteFileReplicas(fileInfo, fileSystemName, relativePath)) {
                        return Result.fail("删除文件副本失败");
                    }
                } else {
                    if (!deleteDirectoryRecursively(fileInfo, fileSystemName, relativePath)) {
                        return Result.fail("删除文件夹及其内容失败");
                    }
                }
                // 删除数据库记录
                fileInfoMapper.delete(new LambdaQueryWrapper<FileInfo>()
                        .eq(FileInfo::getFilePath, fileInfo.getFilePath())
                        .eq(FileInfo::getFileSystemName, fileInfo.getFileSystemName())
                );
            }

            return Result.success("删除文件系统下的所有内容成功");
        }

        // 在数据库里找到文件的元数据
        FileInfo fileInfo = fileInfoMapper.selectOne(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, fullPath)
                .eq(FileInfo::getFileSystemName, fileSystemName)
        );
        if (fileInfo == null) {
            return Result.fail("文件或文件夹不存在");
        }
        // 判断是文件还是文件夹
        if (fileInfo.getFolderType() == 0) {
            // 是文件，删除所有副本
            boolean deleted = deleteFileReplicas(fileInfo, fileSystemName, path);
            if (!deleted) {
                return Result.fail("删除文件副本失败");
            }
        } else {
            // 是文件夹，递归删除所有子目录和文件
            boolean deleted = deleteDirectoryRecursively(fileInfo, fileSystemName, path);
            if (!deleted) {
                return Result.fail("删除文件夹及其内容失败");
            }
        }

        // 删除数据库中的文件或文件夹记录
        fileInfoMapper.delete(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, fullPath)
                .eq(FileInfo::getFileSystemName, fileSystemName)
        );

        return Result.success("删除成功");
    }

    @Override
    public Result openFile(String fileSystemName, String path) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        FileInfo fileInfo = getOne(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, rootPath + "/" + fileSystemName + path)
                .eq(FileInfo::getFileSystemName, fileSystemName)
        );
        if (fileInfo == null) {
            return Result.fail("文件不存在");
        }
        //三个副本的位置
        List<String> locations = Arrays.asList(rootPath + fileInfo.getCopyLocationOne(), rootPath + fileInfo.getCopyLocationTwo(), rootPath + fileInfo.getCopyLocationThree());
        return Result.success(locations);
    }

    //主要是更新文件大小信息
    @Override
    public Result updateFileInfo(String fileSystemName, String path, FileInfo fileInfo) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        fileInfoMapper.update(fileInfo, new LambdaUpdateWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, rootPath + "/" + fileSystemName + path)
                .eq(FileInfo::getFileSystemName, fileSystemName));
        return Result.success("更新成功");
    }

    // 删除文件的所有副本
    private boolean deleteFileReplicas(FileInfo fileInfo, String fileSystemName, String path) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        // 遍历每一个副本位置，尝试删除文件
        try {
            if (fileInfo.getCopyLocationOne() != null) {
                File file = new File(rootPath + "/" + fileInfo.getCopyLocationOne() + "/" + fileSystemName + path);
                if (file.exists() && !file.delete()) {
                    log.error("无法删除文件副本1: " + file.getAbsolutePath());
                    return false;
                } else if (file.exists()) {
                    log.error("文件副本1仍然存在: " + file.getAbsolutePath());
                    return false;
                }
            }
            if (fileInfo.getCopyLocationTwo() != null) {
                File file = new File(rootPath + "/" + fileInfo.getCopyLocationTwo() + "/" + fileSystemName + path);
                if (file.exists() && !file.delete()) {
                    log.error("无法删除文件副本2: " + file.getAbsolutePath());
                    return false;
                } else if (file.exists()) {
                    log.error("文件副本2仍然存在: " + file.getAbsolutePath());
                    return false;
                }
            }
            if (fileInfo.getCopyLocationThree() != null) {
                File file = new File(rootPath + "/" + fileInfo.getCopyLocationThree() + "/" + fileSystemName + path);
                if (file.exists() && !file.delete()) {
                    log.error("无法删除文件副本3: " + file.getAbsolutePath());
                    return false;
                } else if (file.exists()) {
                    log.error("文件副本3仍然存在: " + file.getAbsolutePath());
                    return false;
                }
            }
        } catch (Exception e) {
            log.error("删除文件副本时出错: " + e.getMessage());
            return false;
        }

        return true;
    }

    // 递归删除文件夹及其内容
    private boolean deleteDirectoryRecursively(FileInfo fileInfo, String fileSystemName, String currentPath) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        // 找到该文件夹下的所有文件和子文件夹
        List<FileInfo> children = fileInfoMapper.selectList(new LambdaQueryWrapper<FileInfo>()
                .likeRight(FileInfo::getFilePath, fileInfo.getFilePath() + "/")
                .eq(FileInfo::getFileSystemName, fileInfo.getFileSystemName())
        );

        // 递归删除所有子文件和子文件夹
        for (FileInfo child : children) {
            String childPath = child.getFilePath();
            String relativeChildPath = childPath.replaceFirst("^" + rootPath + "/" + fileSystemName, "");

            if (child.getFolderType() == 0) {
                if (!deleteFileReplicas(child, fileSystemName, relativeChildPath)) {
                    return false;
                }
            } else {
                if (!deleteDirectoryRecursively(child, fileSystemName, relativeChildPath)) {
                    return false;
                }
            }
            // 删除数据库记录
            fileInfoMapper.delete(new LambdaQueryWrapper<FileInfo>()
                    .eq(FileInfo::getFilePath, child.getFilePath())
                    .eq(FileInfo::getFileSystemName, child.getFileSystemName())
            );
        }

        // 删除当前文件夹本身的副本
        String relativeCurrentPath = currentPath.replaceFirst("^" + rootPath + "/" + fileSystemName, "");
        return deleteFileReplicas(fileInfo, fileSystemName, relativeCurrentPath);
    }

    @Override
    public Result getFileInfo(String fileSystemName, String path) {
        if (fileSystemName == null || fileSystemName.isEmpty())
            fileSystemName = "default";
        String fullPathInDatabase;
        if (path.startsWith("/")) {
            fullPathInDatabase = rootPath + "/" + fileSystemName + path;
        } else {
            fullPathInDatabase = rootPath + "/" + fileSystemName + "/" + path;
        }

        FileInfo fileInfo = fileInfoMapper.selectOne(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, fullPathInDatabase)
                .eq(FileInfo::getFileSystemName, fileSystemName)
        );
        if (fileInfo == null) {
            return Result.fail("文件不存在");
        }
        return Result.success(fileInfo);
    }


    //判断数据库里有没有一模一样的数据
    public boolean isFileExist(FileInfo fileInfo) {
        Long l = fileInfoMapper.selectCount(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getFilePath, fileInfo.getFilePath())
                .eq(FileInfo::getFileName, fileInfo.getFileName())
                .eq(FileInfo::getFileSystemName, fileInfo.getFileSystemName())
                .eq(FileInfo::getFolderType, fileInfo.getFolderType())
                .eq(FileInfo::getCopyLocationOne, fileInfo.getCopyLocationOne())
                .eq(FileInfo::getCopyLocationTwo, fileInfo.getCopyLocationTwo())
                .eq(FileInfo::getCopyLocationThree, fileInfo.getCopyLocationThree())
        );
        return l > 0;
    }
}
